package org.ovirt.jenkins;

import hudson.Extension;
import hudson.model.Computer;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.model.Label;
import hudson.model.Node;
import hudson.model.Node.Mode;
import hudson.slaves.Cloud;
import hudson.slaves.NodeProperty;
import hudson.slaves.NodeProvisioner.PlannedNode;
import hudson.slaves.RetentionStrategy;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;

import org.kohsuke.stapler.DataBoundConstructor;
import org.ovirt.jenkins.model.VM;

public class OVirtCloud extends Cloud {

	private final String restApiUrl;
	private final String apiUser;
	private final String apiPass;
	private final String poolName;

	private final String poolUserName;
	private final String poolUserPasswd;

	@DataBoundConstructor
	public OVirtCloud(final String name, final String restApiUrl,
			final String apiUser, final String apiPass, final String poolName,
			final String poolUserName, final String poolUserPasswd) {
		super(name);
		this.restApiUrl = restApiUrl;
		this.poolName = poolName;
		this.apiPass = apiPass;
		this.apiUser = apiUser;
		this.poolUserName = poolUserName;
		this.poolUserPasswd = poolUserPasswd;
	}

	@Override
	public Collection<PlannedNode> provision(Label label, int excessWorkload) {
		try {
			final OVirtService service = getService();
			List<VM> vms = service.listVms(poolName, getLabel(label));
			ArrayList<PlannedNode> ret = new ArrayList<PlannedNode>();
			for (final VM vm : vms) {
				ret.add(new PlannedNode(vm.getName(),
						Computer.threadPoolForRemoting
								.submit(new Callable<Node>() {

									public Node call() throws Exception {
										final OVirtSlave slave = new OVirtSlave(
												vm.getName(),
												vm.getDescription() == null ? ""
														: vm.getDescription(),
												"",
												1,
												Mode.EXCLUSIVE,
												"",
												new OVirtComputerLauncher(
														restApiUrl, apiUser,
														apiPass, poolUserName,
														poolUserPasswd),
												new RetentionStrategy.Demand(
														60000, 60000),
												new ArrayList<NodeProperty<?>>(),
												service, vm);
										Hudson.getInstance().addNode(slave);
										return slave;
									}
								}), excessWorkload));
			}
			return ret;
		} catch (final IOException e) {
			// TODO: log error!
			return null;
		}
	}

	private String getLabel(Label label) {
		return label == null ? null : label.getName();
	}

	@Override
	public boolean canProvision(final Label label) {
		try {
			final OVirtService service = getService();
			return !service.listVms(poolName, getLabel(label)).isEmpty();
		} catch (IOException e) {
			// TODO: log communication problem with the oVirt service!
			return false;
		}
	}

	private OVirtService getService() throws IOException {
		return new OVirtService(restApiUrl, apiUser, apiPass);
	}

	@Extension
	public static class OVirtDescriptor extends Descriptor<Cloud> {

		@Override
		public String getDisplayName() {
			return "oVirt";
		}

	}

	public String getRestApiUrl() {
		return restApiUrl;
	}

	public String getApiUser() {
		return apiUser;
	}

	public String getApiPass() {
		return apiPass;
	}

	public String getPoolName() {
		return poolName;
	}

	public String getPoolUserName() {
		return poolUserName;
	}

	public String getPoolUserPasswd() {
		return poolUserPasswd;
	}

}
